package com.st.mall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.st.common.dubboProvider.DemoService;
import com.st.mall.product.dao.CategoryBrandRelationDao;
import com.st.mall.product.vo.Catalogs2Vo;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.st.common.utils.PageUtils;
import com.st.common.utils.Query;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import com.st.mall.product.dao.CategoryDao;
import com.st.mall.product.entity.CategoryEntity;
import com.st.mall.product.service.CategoryService;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;


@DubboService(interfaceClass = DemoService.class)
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService, DemoService {

    @Autowired
    private CategoryBrandRelationDao dao;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(new Query<CategoryEntity>().getPage(params), new QueryWrapper<CategoryEntity>());

        return new PageUtils(page);
    }

    /**
     * 查找树形菜单
     */
    @Override
    public List<CategoryEntity> returnTree() {
        //查找所有菜单
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);

        //查找所有一级菜单
        List<CategoryEntity> collect = categoryEntities.stream().filter((entity) -> {
            return entity.getParentCid() == 0;
        }).map(getSon -> {
            getSon.setSonCategory(getSonCategoryList(getSon, categoryEntities));
            return getSon;
        }).sorted(Comparator.comparingInt(CategoryEntity::getSort)).collect(Collectors.toList());
        return collect;
    }

    /**
     * 逻辑删除，加被删除菜单是否被引用判断
     */

    @Override
    public void removeLogicByIds(List<Long> asList) {
        //TODO 被删除菜单是否被引用判断
        baseMapper.deleteBatchIds(asList);
    }

    //更新数据库时，同步删除缓存
    @CacheEvict(value = "category",allEntries = true)       //删除某个分区下的所有数据
    @Override
    public void updateMore(CategoryEntity category) {
        this.updateById(category);
        //同步更新CategoryBrandRelationEntity对应表的catelogName
        dao.updateCateLogName(category.getCatId(), category.getName());
    }

    /**
     * 递归查找子菜单
     */
    private List<CategoryEntity> getSonCategoryList(CategoryEntity root, List<CategoryEntity> list) {
        List<CategoryEntity> collect = list.stream().filter(entity -> {
            return root.getCatId().equals(entity.getParentCid());
        }).map(getSon -> {
            //递归查找子菜单
            getSon.setSonCategory(getSonCategoryList(getSon, list));
            return getSon;
            //给子菜单排个序
        }).sorted(Comparator.comparing(CategoryEntity::getSort, Comparator.nullsLast(Integer::compareTo))).collect(Collectors.toList());
        return collect;
    }

    //Cacheable注解,key表示redis缓存的key的名字，sync表示开启本地锁
    @Cacheable(value = "category",key = "#root.methodName",sync = true)
    @Override
    public List<CategoryEntity> getLevel1Categories() {
        List<CategoryEntity> categoryEntities = this.baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        return categoryEntities;
    }

    //TODO: 这种redis调用会产生堆外内存溢出，即outOfDirectMemoryError
    //1)、springboot2.0以后默认使用lettuce操作redis的客户端，它使用netty进行网络通信
    //2)、lettuce的bug导致netty堆外内存溢出   可设置：-Dio.netty.maxDirectMemory
    //解决方案：不能直接使用-Dio.netty.maxDirectMemory去调大堆外内存
    //1)、升级lettuce客户端到5.2.0以上。      2）、排除lettuce，引入使用jedis
//    @Override
//    public Map<String, List<Catalogs2Vo>> getCatalogJson() {
//        // 1.从缓存中读取分类信息
//        String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON");
//        if (StringUtils.isEmpty(catalogJSON)) {
//            // 2. 缓存中没有，查询数据库
//            Map<String, List<Catalogs2Vo>> catalogJsonFromDB = getCatalogJsonFromDB();
//            //当db中也没有数据时设置空结果，并设置失效时间，防止缓存穿透
//            if (catalogJsonFromDB.size() == 0) {
//                stringRedisTemplate.opsForValue().set("catalogJSON", "0", 1, TimeUnit.DAYS);
//            } else {
//                // 3. 查询到的数据存放到缓存中，将对象转成 JSON 存储
//                // 设置随机过期时间防止缓存雪崩
//                stringRedisTemplate.opsForValue().set("catalogJSON", JSON.toJSONString(catalogJsonFromDB), 1, TimeUnit.DAYS);
//            }
//            return catalogJsonFromDB;
//        }
//        //TypeReference转换为要返回的对象
//        return JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catalogs2Vo>>>() {
//        });
//    }

    /**
     * 加缓存前,只读取数据库的操作
     *
     */
    @Cacheable(value = "category",key = "#root.methodName")
//    public Map<String, List<Catalogs2Vo>> getCatalogJsonFromDB() {
    public Map<String, List<Catalogs2Vo>> getCatalogJson() {

        System.out.println("查询了数据库");

        // 性能优化：将数据库的多次查询变为一次
        List<CategoryEntity> selectList = this.baseMapper.selectList(null);

        //1、查出所有分类
        //1、1）查出所有一级分类
        List<CategoryEntity> level1Categories = getParentCid(selectList, 0L);

        //封装数据
        Map<String, List<Catalogs2Vo>> parentCid = level1Categories.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
            //1、每一个的一级分类,查到这个一级分类的二级分类
            List<CategoryEntity> categoryEntities = getParentCid(selectList, v.getCatId());

            //2、封装上面的结果
            List<Catalogs2Vo> catalogs2Vos = null;
            if (categoryEntities != null) {
                catalogs2Vos = categoryEntities.stream().map(l2 -> {
                    Catalogs2Vo catalogs2Vo = new Catalogs2Vo(v.getCatId().toString(), null, l2.getCatId().toString(), l2.getName().toString());

                    //1、找当前二级分类的三级分类封装成vo
                    List<CategoryEntity> level3Catelog = getParentCid(selectList, l2.getCatId());

                    if (level3Catelog != null) {
                        List<Catalogs2Vo.Category3Vo> category3Vos = level3Catelog.stream().map(l3 -> {
                            //2、封装成指定格式
                            Catalogs2Vo.Category3Vo category3Vo = new Catalogs2Vo.Category3Vo(l2.getCatId().toString(), l3.getCatId().toString(), l3.getName());

                            return category3Vo;
                        }).collect(Collectors.toList());
                        catalogs2Vo.setCatalog3List(category3Vos);
                    }

                    return catalogs2Vo;
                }).collect(Collectors.toList());
            }

            return catalogs2Vos;
        }));

        return parentCid;
    }

    private List<CategoryEntity> getParentCid(List<CategoryEntity> selectList, Long parentCid) {
        List<CategoryEntity> categoryEntities = selectList.stream().filter(item -> item.getParentCid().equals(parentCid)).collect(Collectors.toList());
        return categoryEntities;
        // return this.baseMapper.selectList(
        //         new QueryWrapper<CategoryEntity>().eq("parent_cid", parentCid));
    }

}